/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * sys.c
 */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "common.h"

static Statement *next_macro_statement(Queue *repQueue, String macro) {
	String str = alloc_string(20);
	Statement *statement = NULL;

	if (*macro == PAREN_L) {
		int i = 0;
		while (*macro != PAREN_R) {
			str[i++] = *macro++;
		}
		str[i++] = *macro;
		str[i++] = '\0';

		statement = parse(str);
		return (Statement *)find_value(repQueue, get_arg_name(statement));
	}

	return NULL;
}

static int is_macro_char(char c) {
	return isascii(c) && isupper(c);
}

static String get_macro_word(char *p) {
	String buf = alloc_string(20);
	String r = buf;
	while (*p && is_macro_char(*p)) {
		*buf++ = *p++;
	}
	*buf = '\0';
	return r;
}

static String get_args(char *p, char *q) {
	if (p >= q) {
		return NULL;
	}
	String buf = alloc_string(20);
    String r = buf;
	while (p != q) {
		*buf++ = *p++;
	}
	*buf++ = *q;
	*buf = '\0';
	return r;
}

static String parse_macro(Queue *repQueue, String macro) {
	String buf = init_string();
	Statement **sp = (Statement **)malloc(4 * sizeof(Statement *));
	Statement *s = NULL;
	int args = 0;
	String temp = NULL;
	char c;
	char *p;
	char *q;
	String argStr = NULL;

	while (*macro) {
		c = *macro;

		if (c == PAREN_L) {
			s = next_macro_statement(repQueue, macro);
			p = strchr(macro, PAREN_R);
			if (!p)
				return NULL;
			macro = p + 1;

			temp = to_string_statement(s);
			buf = dyna_strcat(buf, temp);
		} else if (is_macro_char(c)) {
			String macroWord = get_macro_word(macro);
			macro += strlen(macroWord);
            args = 0;

			p = strchr(macro, '[');
			if (!p)
				return NULL;
			macro = p + 1;

			q = strchr(macro, ']');
			if (!q)
				return NULL;
			macro = q + 1;

			argStr = get_args(p + 1, q - 1);
			s = next_macro_statement(repQueue, argStr);
            *sp = s;
			p = strchr(argStr, ',');
            args++;
			while (p != NULL) {
				argStr = p + 1;
				s = next_macro_statement(repQueue, argStr);
                *(sp + args) = s;
                p = strchr(argStr, ',');
                args++;
			}
            *(sp + args) = NULL;

			temp = macro_function(macroWord, args, sp);
			buf = dyna_strcat(buf, temp);
		} else if (c == '\\') {
			macro++;
			buf = append_char(buf, *macro);
			if (*macro)
				macro++;
		} else {
			buf = append_char(buf, *macro);
			if (*macro)
				macro++;
		}
	}

	return buf;
}

String eval_sys(Mapping *m, Queue *repQueue) {
	String name = copy_string(m->sys->name);
	String function = copy_string(m->sys->function);
	String mappingStr = copy_string(m->sys->mapping);
	String run = copy_string(m->sys->run);
	String eval = copy_string(m->sys->eval);
	Mapping *mapping = NULL;

	String* array = NULL;
	String mappingSource;
	String mappingTarget;

	if (!equals_string(mappingStr, "NIL")) {
		mapping = alloc_mapping();
		mapping->sys = NULL;
		if (strcmp(name, MACRO_DEF_MACRO) == 0) {
			mapping->isMacro = TRUE;
		} else {
			mapping->isMacro = FALSE;
		}

		array = split_string_once(mappingStr, '>');
		mappingSource = array[0];
		mappingTarget = array[1];

		if (!equals_string(function, "NIL")) {
			mapping->functionName = parse_macro(repQueue, function);
		}
		mapping->source = parse_macro(repQueue, trim_string(mappingSource));
		mapping->target = parse_macro(repQueue, trim_string(mappingTarget));
		mapping->sourceStatement = parse(mapping->source);
		mapping->targetStatement = parse(mapping->target);
		mapping->starter = to_dfa(mapping->sourceStatement);

		add_mapping_to_trie(mapping);
	}

	if (!equals_string(run, "NIL")) {
		parse_macro(repQueue, run);
	}

	if (!equals_string(eval, "NIL")) {
		return parse_macro(repQueue, eval);
	} else {
		return "";
	}
}
